<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      const arr = [
        1,
        2,
        2,
        { id: 0, name: "孙悟空" },
        { id: 0, name: "孙悟空" },
        { id: 1, name: "猪八戒" },
        { id: 1, name: "猪八戒" },
      ];
      function fn() {
        console.log("fn");
      }
      arr.push(fn);
      arr.push(fn);
      console.log("arr", arr);
      const arr2 = [...new Set(arr)]; // 可以看到，set 对于数组内的对象，没办法去重
      console.log("arr2", arr2);

      /**
       * 思路：
       * 1. 定义一个结果数组 result，用于存放去重后的元素
       * 2. 定义一个 seen 集合，用于存储已经处理过的元素
       * 3. 遍历原数组 arr，对于每个元素 item：
       *    a. 如果 item 是原始数据类型（如数字、字符串、布尔值等），直接判断 seen 中是否已存在该值，不存在则添加到 result 和 seen
       *    b. 如果 item 是对象：
       *       - 如果提供了第二个参数 key（对象的某个属性），则根据该属性值进行去重
       *       - 否则，将 item 的属性按顺序排序后使用 JSON.stringify 进行去重
       *    c. 如果 item 是函数，直接比较函数引用
       * 4. 返回 result
       *
       * 注意：
       * - 对于对象，优先使用 key 参数进行去重
       * - 如果没有提供 key 参数，对对象的属性排序后再使用 JSON.stringify
       * - 对于函数，直接比较函数引用即可
       */
      function uniq(arr, key) {
        const result = [];
        const seen = new Set(); // 用于存储已经处理过的元素

        for (const item of arr) {
          let itemKey;

          if (typeof item === "object" && item !== null) {
            // 对象
            if (key && item.hasOwnProperty(key)) {
              // 如果提供了 key 参数，根据 key 值进行去重
              // 为对象的 key 值添加特殊标识符，避免与原始数据类型冲突
              itemKey = `object-${key}-${item[key]}`;
            } else {
              // 没有提供 key 参数，对对象的属性排序后使用 JSON.stringify
              itemKey = JSON.stringify(Object.keys(item).sort().reduce((acc, k) => {
                acc[k] = item[k];
                return acc;
              }, {}));
            }
          } else if (typeof item === "function") {
            // 函数，直接比较函数引用
            itemKey = item.toString();
          } else {
            // 原始数据类型，直接使用值
            itemKey = item;
          }

          // 判断是否已处理过
          if (!seen.has(itemKey)) {
            seen.add(itemKey);
            result.push(item);
          }
        }

        return result;
      }

      // 测试
      const arr3 = uniq(arr); // 未指定 key，使用默认的去重方式
      console.log("arr3", arr3);

      const arr4 = uniq(arr, "id"); // 指定 key 为 "id"，根据 id 去重
      console.log("arr4", arr4);
    </script>
  </body>
</html>
